package net.alche.fa.core;

import java.util.List;
import net.alche.fa.core.model.FaValue;
import java.util.ArrayList;
import net.alche.fa.core.model.FaParam;
import java.lang.reflect.Array;

@SuppressWarnings("all")
public class FaClass {
  FaClassConf conf;
  SageStone thisSage;
  boolean staticFlag = false;
  FaPlatform plat;

  public FaClass(FaPlatform plat) {
    this.plat = plat;
  }

  public FaClass(String className) {
    this.conf = new FaClassConf();
    this.conf.name = className;
  }

  public void runMain() throws FaException {
    FaMethod faMethod = conf.getMethod("main", null);
    if (faMethod == null) {
      throw new FaException(FaException.LESS_METHOD, "没有main函数");
    }
    SageStone st = new SageStone(this.plat, faMethod.getLogicList(), this);
    st.run(null);
  }

  public void init(CodeLevel lvl) throws FaException {
    thisSage = new SageStone(this.plat, conf.getInitLogicList(), this);
    if (lvl != null) {
      thisSage.run(lvl.instanceDeep());
    } else {
      thisSage.run(null);
    }
  }

  public Object invoke(String methodName, Object... pars) {
    CodeLevel lvl = new CodeLevel();
    // lvl.sageBiz = this.thisSage.sageBiz;
    lvl.uuid = "01";
    List<FaValue> params = null;
    if (pars != null) {
      params = new ArrayList<>();
      for (Object obj : pars) {
        FaValue val = new FaValue(obj);
        if (val.type == FaValue._OBJECT) {
          if (obj != null) {
            val.setObjType(obj.getClass().getSimpleName());
          }
        }
        params.add(val);
      }
    }
    try {
      FaValue backVal = this.call(methodName, params, lvl);
      if (backVal != null) {
        return backVal.getJava();
      } else {
        return null;
      }
    } catch (Exception ex) {
      ex.printStackTrace();
      return null;
    }
  }

  public SageStone instance(Object... pars) throws FaException {
    FaClass fac = this.conf.instance(null);
    SageStone st = fac.getThisSage();
    return st;
  }

  public FaValue call(String methodName, List<FaValue> params, CodeLevel cl) throws FaException {
    FaMethod faMethod = conf.getMethod(methodName, params);
    if (faMethod == null && this.getThisSage() != null) {
      SageStone stOuter = this.getThisSage()._outer;
      if (stOuter != null) {
        FaMethod outerMethod = stOuter.faClass.conf.getMethod(methodName, params);
        if (outerMethod != null) {
          return stOuter.getFaClass().call(methodName, params, cl);
        }
      }
    }
    if (faMethod == null) {
      throw new FaException(FaException.LESS_METHOD,
          "[FaClass.call.lessMethod]: " + this.conf.name + "." + methodName + "( " + parStr(params) + " )");
    }
    return call(faMethod, params, cl);
  }

  public FaValue call(FaMethod faMethod, List<FaValue> params, CodeLevel lvl) throws FaException {
    CodeLevel cl = lvl.instanceDeep();
    cl.debugFall.setNum(faMethod.getNum());
    cl.debugFall.setCodeStr(faMethod.getCodeStr());
    cl.debugFall.setPrefix(this.getConf().name + "." + faMethod.getMethodName() + "()");
    if (conf.isSqlDao()) {
      return this.plat.getSqlDo().doDao(this, faMethod, params, cl);
    }
    SageStone st = new SageStone(this.plat, faMethod.getLogicList(), this);
    if (faMethod.getParamList() != null && faMethod.getParamList().size() > 0) {
      int i = 0;
      for (FaParam fp : faMethod.getParamList()) {
        FaValue fav = new FaValue();
        fav.setVarName(fp.varName);
        if (fp.isDynamic) {
          fav.setType(FaValue._ARR);
          int size = params.size();
          Object arrObj = null;
          if (fp.type == FaValue._SAGE) {
            arrObj = Array.newInstance(FaValue.class, size - i);
            int index = 0;
            while (i < size) {
              FaValue pp = params.get(i);
              Array.set(arrObj, index, pp);
              i++;
              index++;
            }
          } else {
            arrObj = Array.newInstance(this.plat.getFaDo().getObjClass(fp.objTypeName), size - i);
            int index = 0;
            while (i < size) {
              FaValue pp = params.get(i);
              Array.set(arrObj, index, pp.getValue());
              i++;
              index++;
            }
          }
          fav.setObjType(fp.objTypeName + "[]");
          fav.setObjValue(arrObj);
          fav.setNull(false);
        } else {
          fav.setObjType(fp.objTypeName);
          fav.setType(fp.type);
          fav.giveValue(params.get(i++), this.plat);
        }
        // GiveDo.give(fav, )
        st.putVar(fp.varName, fav);
      }
    }
    return st.run(cl);
  }

  private String parStr(List<FaValue> params) {
    if (params == null || params.size() <= 0) {
      return "";
    }
    String str = "";
    for (FaValue val : params) {
      if (!str.equals("")) {
        str += " , ";
      }
      if (val.getType() == FaValue._OBJECT) {
        str += val.getObjType();
      } else {
        str += FaValue.getCharStr(val.getType());
      }
    }
    return str;
  }

  public FaClassConf getConf() {
    return conf;
  }

  public void setConf(FaClassConf conf) {
    this.conf = conf;
  }

  public SageStone getThisSage() {
    return thisSage;
  }

  public void setThisSage(SageStone thisSage) {
    this.thisSage = thisSage;
  }

  public FaValue getStaticVal(String varName, CodeLevel lvl) throws FaException {
    if (this.conf.getStaticSage() == null) {
      return null;
    }
    return this.conf.getStaticSage().getVarData(varName, lvl);
  }

  public boolean isStatic() {
    return staticFlag;
  }

  public void setStatic(boolean isStatic) {
    this.staticFlag = isStatic;
  }

  public FaPlatform getPlat() {
    return this.plat;
  }

  public void setPlat(FaPlatform plat) {
    this.plat = plat;
  }
}